package com.itmck.lock;

import org.springframework.data.redis.core.*;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author Lin
 * @date 2021-08-06
 */
public class RedisTemplateClient implements IRedisClient {
    private RedisTemplate<String, Object> redisTemplate;
    private ValueOperations valueOperations;
    private HashOperations hashOperations;
    private ListOperations listOperations;
    private SetOperations setOperations;

    public RedisTemplateClient(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.valueOperations = redisTemplate.opsForValue();
        this.hashOperations = redisTemplate.opsForHash();
        this.listOperations = redisTemplate.opsForList();
        this.setOperations = redisTemplate.opsForSet();
    }

    @Override
    public long del(String... keys) {
        List<String> list = Arrays.asList(keys);
        return redisTemplate.delete(list);
    }

    @Override
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    @Override
    public boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }

    @Override
    public boolean expire(String key, int secTTL) {
        return redisTemplate.expire(key, secTTL, TimeUnit.SECONDS);
    }

    @Override
    public long getExpire(String key) {
        return redisTemplate.getExpire(key);
    }

    @Override
    public long append(String key, String value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

    @Override
    public boolean set(String key, String value) {
        return set(key, (Object) value);
    }

    @Override
    public boolean set(String key, Object value) {
        valueOperations.set(key, value);
        return true;
    }

    @Override
    public boolean setnx(String key, String value) {
        return setnx(key, (Object) value);
    }

    @Override
    public boolean setnx(String key, Object value) {
        return valueOperations.setIfAbsent(key, value);
    }

    @Override
    public boolean setNxExAtomic(String key, Object value, long secTTL) {
        return valueOperations.setIfAbsent(key, value, secTTL, TimeUnit.SECONDS);
    }

    @Override
    public boolean set(String key, String value, long secTTL) {
        return set(key, (Object) value, secTTL);

    }

    @Override
    public boolean set(String key, Object value, long secTTL) {
        valueOperations.set(key, value, secTTL, TimeUnit.SECONDS);
        return true;
    }

    @Override
    public String get(String key) {
        Object value = getObject(key);
        return value == null ? null : value.toString();
    }

    @Override
    public Object getObject(String key) {
        return valueOperations.get(key);
    }

    @Override
    public long strlen(String key) {
        return valueOperations.size(key);
    }

    @Override
    public long incr(String key) {
        return valueOperations.increment(key);
    }

    @Override
    public long incrby(String key, int increment) {
        return valueOperations.increment(key, increment);
    }

    @Override
    public long decr(String key) {
        return valueOperations.decrement(key);
    }

    @Override
    public long decrby(String key, int decrement) {
        return valueOperations.decrement(key, decrement);
    }

    @Override
    public long hdel(String key, String... fields) {
        return hashOperations.delete(key, (Object[])fields);
    }

    @Override
    public long hincrby(String key, String field, long value) {
        return hashOperations.increment(key, field, value);
    }

    @Override
    public boolean hmset(String key, Map<String, String> map) {
        hashOperations.putAll(key, map);
        return true;
    }

    @Override
    public Map<String, String> hmget(String key, String... fields) {
        Object[] objects = fields;
        List<Object> list = Arrays.asList(objects);
        List<Object> values = hashOperations.multiGet(key, list);
        Map<String, String> map = new HashMap<>();
        for (int i = 0; i < list.size(); i++) {
            Object field = list.get(i);
            Object value = values.get(i);
            if (field != null && value != null) {
                map.put(field.toString(), value.toString());
            }
        }
        return map;
    }

    @Override
    public Set<String> hvals(String key) {
        List<Object> list = hashOperations.values(key);
        Set<String> values = new HashSet<>();
        for (Object obj : list) {
            if (obj != null) {
                values.add(obj.toString());
            }
        }
        return values;
    }

    @Override
    public boolean hexists(String key, String field) {
        return hashOperations.hasKey(key, field);
    }

    @Override
    public String hget(String key, String field) {
        Object obj = hashOperations.get(key, field);
        return obj == null ? null : obj.toString();
    }

    @Override
    public Set<String> hkeys(String key) {
        Set<Object> list = hashOperations.keys(key);
        Set<String> keys = new HashSet<>();
        for (Object obj : list) {
            if (obj != null) {
                keys.add(obj.toString());
            }
        }
        return keys;
    }

    @Override
    public long hset(String key, String field, String value) {
        boolean absent = hashOperations.putIfAbsent(key, field, value);
        if (absent) {
            return 1;
        }
        hashOperations.put(key, field, value);
        return 0;
    }

    @Override
    public Map<String, String> hgetAll(String key) {
        Map<Object, Object> entries = hashOperations.entries(key);
        Map<String, String> map = new HashMap<>();
        for (Map.Entry<Object, Object> entry : entries.entrySet()) {
            Object field = entry.getKey();
            Object value = entry.getValue();
            map.put(field.toString(), value == null ? null : value.toString());
        }
        return map;
    }

    @Override
    public long hlen(String key) {
        return hashOperations.size(key);
    }

    @Override
    public long hsetnx(String key, String field, String value) {
        return hashOperations.putIfAbsent(key, field, value) ? 1 : 0;
    }

    @Override
    public long llen(String key) {
        return listOperations.size(key);
    }

    @Override
    public String lpop(String key) {
        return toString(listOperations.leftPop(key));
    }

    @Override
    public String rpop(String key) {
        return toString(listOperations.rightPop(key));
    }

    @Override
    public String[] blpop(long secTTL, String... keys) {
        for (String key : keys) {
            Object value = listOperations.leftPop(key, secTTL, TimeUnit.SECONDS);
            if (value != null) {
                return new String[]{key, value.toString()};
            }
        }
        return null;
    }

    @Override
    public String[] brpop(long secTTL, String... keys) {
        for (String key : keys) {
            Object value = listOperations.rightPop(key, secTTL, TimeUnit.SECONDS);
            if (value != null) {
                return new String[]{key, value.toString()};
            }
        }
        return null;
    }

    @Override
    public long lpush(String key, String... values) {
        Object[] objs = values;
        return listOperations.leftPushAll(key, objs);
    }

    @Override
    public long rpush(String key, String... values) {
        Object[] objs = values;
        return listOperations.rightPushAll(key, objs);
    }

    @Override
    public long lpushx(String key, String value) {
        return listOperations.leftPushIfPresent(key, value);
    }

    @Override
    public long rpushx(String key, String value) {
        return listOperations.rightPushIfPresent(key, value);
    }

    @Override
    public String[] lrange(String key, int start, int end) {
        List<Object> objects = listOperations.range(key, start, end);
        String[] values = new String[objects.size()];
        int i = 0;
        for (Object obj : objects) {
            values[i++] = toString(obj);
        }
        return values;
    }

    @Override
    public String lindex(String key, int index) {
        return toString(listOperations.index(key, index));
    }

    @Override
    public boolean lset(String key, int index, String value) {
        listOperations.set(key, index, value);
        return true;
    }

    @Override
    public long sadd(String key, String... member) {
        Object[] objects = member;
        return setOperations.add(key, objects);
    }

    @Override
    public Set<String> smembers(String key) {
        return setOperations.members(key);
    }

    @Override
    public long srem(String key, String member) {
        return setOperations.remove(key, member);
    }

    @Override
    public String spop(String key) {
        return toString(setOperations.pop(key));
    }

    @Override
    public long smove(String srckey, String dstkey, String member) {
        return setOperations.move(srckey, member, dstkey) ? 1 : 0;
    }

    @Override
    public long scard(String key) {
        return setOperations.size(key);
    }

    @Override
    public boolean sismember(String key, String member) {
        return setOperations.isMember(key, member);
    }

    @Override
    public Set<String> sinter(String[] keys) {
        Collection<String> otherKeys = dropFirst(keys);
        return setOperations.intersect(keys[0], otherKeys);
    }

    @Override
    public long sinterstore(String dstkey, String[] keys) {
        Collection<String> otherKeys = dropFirst(keys);
        return setOperations.intersectAndStore(keys[0], otherKeys, dstkey);
    }

    private Collection<String> dropFirst(String[] keys) {
        if (null == keys || keys.length < 2) {
            throw new IllegalArgumentException("keys不可为空!");
        }
        Collection<String> otherKeys = new ArrayList<>();
        for (int i = 1; i < keys.length; i++) {
            otherKeys.add(keys[i]);
        }
        return otherKeys;
    }

    @Override
    public Set<String> sunion(String[] keys) {
        Collection<String> otherKeys = dropFirst(keys);
        return setOperations.union(keys[0], otherKeys);
    }

    @Override
    public long sunionstore(String dstkey, String[] keys) {
        Collection<String> otherKeys = dropFirst(keys);
        return setOperations.unionAndStore(keys[0], otherKeys, dstkey);
    }

    @Override
    public Set<String> sdiff(String[] keys) {
        Collection<String> otherKeys = dropFirst(keys);
        return setOperations.difference(keys[0], otherKeys);
    }

    @Override
    public long sdiffstore(String dstkey, String[] keys) {
        Collection<String> otherKeys = dropFirst(keys);
        return setOperations.differenceAndStore(keys[0], otherKeys, dstkey);
    }

    @Override
    public String srandmember(String key) {
        return toString(setOperations.randomMember(key));
    }

    private String toString(Object object) {
        return object == null ? null : object.toString();
    }
}
